/*
 * pn.h
 *
 *  Created on: 08.08.2013
 *      Author: r.leonov
 */

#ifndef PN_H_
#define PN_H_

#include "kl_lib_L15x.h"
#include "ccid.h"
#include "pn_defins.h"
#include "stdarg.h"
#include "string.h"
#include "msgbox.h"

// ==== PN532 GPIOs PINs ====
#define PN_RST_GPIO             GPIOA
#define PN_NSS_GPIO             GPIOA
#define PN_IRQ_GPIO             GPIOA
#define PN_DATA_GPIO            GPIOB
#define PN_MOSI_PIN             15
#define PN_MISO_PIN             14
#define PN_NSS_PIN              8
#define PN_SCK_PIN              13
#define PN_IRQ_PIN              10
#define PN_RST_PIN              3

// ==== IRQ Level watcher ====
#define PN_RPL_IS_READY         (!PinIsSet(PN_IRQ_GPIO,PN_IRQ_PIN))
#define PN_RPL_IS_NOT_READY     ( PinIsSet(PN_IRQ_GPIO,PN_IRQ_PIN))

// ==== Buf Sizes ====
#define PN_MAX_DATA_SIZE        262 // Max Data Lenght 262 byte um. page 29
#define PN_MAX_TFI_PD0_SIZE     2
#define PN_MAX_CMD_SIZE         274 // PN max data size: 264 + 8 (Prologue) + 2 (Epilogue)
#define ATR_BUF_SIZE            36
#define CRD_MSGBOX_SZ           4

#define WITHOUT_DATA            (uint32_t)0

// ========================= Pn Frame Structure ===============================
// ==== Pn Frame ====
// User manual p.28
// ==================
// Prologue (include TFI and PD0 (idCMD)), Data, Epilogue
struct Prologue_t {
    uint8_t Preamble;       // 0x00
    uint8_t SoPCH;          // Start of Packet Code Low - 0x00 always
    uint8_t SoPCL;          // Start of Packet Code High - 0xFF always
    uint8_t NPLC;           // Normal Packet Length Checksum - 0xFF always
    uint8_t NPL;            // Normal Packet Length - 0xFF always
    uint8_t LengthHi;       // MSByte of Length
    uint8_t LengthLo;       // LSByte of Length
    uint8_t LCS;
//    uint8_t TFI;
//    uint8_t idCmd;
};

struct Data_t {
    uint8_t TFI;
    uint8_t PD0;
    uint8_t Buf[PN_MAX_DATA_SIZE];
};

struct XfrData_t {
    uint8_t TFI;
    uint8_t PD0;
    uint8_t Tg;
    uint8_t Buf[PN_MAX_DATA_SIZE-1];
};

struct Epilogue_t {
    uint8_t DCS;
    uint8_t Postamble;      // 0x00
};

struct Pn_Frame_t {
    uint8_t SeqType;        // What is next Read or Write
    union {
        Prologue_t Prologue;
        uint8_t PrologueBuf[8];
    };
    union {
        Data_t Data;
        XfrData_t XfrData;
        uint8_t ComboData[PN_MAX_TFI_PD0_SIZE + PN_MAX_DATA_SIZE];
    };
    union {
        Epilogue_t Epilogue;
        uint8_t EpilogueBuf[2];
    };

    // CalcLCS() - write LCS checksum in Prologue.LCS Byte
    void CalcLCS() { Prologue.LCS = -(Prologue.LengthHi + Prologue.LengthLo); }

    // CalcDCS() - write DCS checksum in Epilogue.DCS Byte
    // IMPORTANT! Before calling this function PData pointer must be set
    void CalcDCS()          {
        Epilogue.DCS = Data.TFI + Data.PD0;
        for(uint32_t i=0; i<GetLength(); i++){
            Epilogue.DCS += Data.Buf[i];
        } // DCS ready
        Epilogue.DCS = -Epilogue.DCS;
    }

    // GetLength() - return the value of count Important Data without TFI and IdCMD
    uint32_t GetLength()    { return (((Prologue.LengthHi << 8) + (Prologue.LengthLo)) - 2); }

} __attribute__ ((__packed__));

// ============================ SPI Structure =================================
// Baudrate division
enum brDiv_t {
    brDiv2   = 0b000,
    brDiv4   = 0b001,
    brDiv8   = 0b010,
    brDiv16  = 0b011,
    brDiv32  = 0b100,
    brDiv64  = 0b101,
    brDiv128 = 0b110,
    brDiv256 = 0b111
};

// SPI related
class SpiPn_t {
public:
    inline void Disable()              { SPI2->CR1 &= ~SPI_CR1_SPE;    }
    inline void Enable()               { SPI2->CR1 |=  SPI_CR1_SPE;    }
    inline void RxNEIrqEnable()        { SPI2->CR2 |=  SPI_CR2_RXNEIE; }
    inline void RxNEIrqDisable()       { SPI2->CR2 &= ~SPI_CR2_RXNEIE; }
    inline void TxEIrqEnable()         { SPI2->CR2 |=  SPI_CR2_TXEIE;  }
    inline void TxEIrqDisable()        { SPI2->CR2 &= ~SPI_CR2_TXEIE;  }
    inline void TxDMAEnable()          { SPI2->CR2 |=  SPI_CR2_TXDMAEN;}
    inline void TxDMADisable()         { SPI2->CR2 &= ~SPI_CR2_TXDMAEN;}
    inline void ClearTxE()             { SPI2->SR  &= ~SPI_SR_TXE;     }
    inline void ClearRxNE()            { SPI2->SR  &= ~SPI_SR_RXNE;    }
    inline void ClearSR()              { SPI2->SR  = 0x02;             }
    inline void SetBaud(brDiv_t brDiv) { SPI2->CR1 |= brDiv << 3;      }
//
//    // Cmd Line
    inline void NssLo()  { PinClear(PN_NSS_GPIO, PN_NSS_PIN); Delay_us(121); }
    inline void NssHi()  { PinSet  (PN_NSS_GPIO, PN_NSS_PIN); }
};

enum StartRx_t { StartRx, DoNotStartRx };
// ==== Card ====
enum CardState_t { crdIdle, crdBusy, crdOff, crdNoCard, crdError };

// ==== Main Pn Structure ====
struct Pn_t {
private:
    uint8_t PnRxBuf[PN_MAX_CMD_SIZE];  // Buf to receive data from card
    uint32_t RxLength;
    // Card Related
    uint8_t NFCID[4], NFCIDLength;
    uint8_t ATSBuf[ATR_BUF_SIZE], ATSLength;
    // Synchronization
    InputQueue IPnRxQueue;
    Thread *PThread;
    bool PnWasOff;
    bool INeedToRecive;
    StartRx_t IStartRx;
    // High Level
    uint8_t CardDetected();
    uint8_t CardIsStillNear();
    uint8_t Identify();
    void ParseATS();
    // ==== Hardware High Lvl ====
    void IActivation();
    void IDeactivation();
    void FieldOn()                  { Cmd(PN_CMD_RF_CONFIGURATION, 2, 0x01, 0x01); }
    void FieldOff()                 { Cmd(PN_CMD_RF_CONFIGURATION, 2, 0x01, 0x00); }
    // ==== Hardware Low Level ====
    inline void IRstLo()            { PinClear(PN_RST_GPIO, PN_RST_PIN); }
    inline void IRstHi()            { PinSet  (PN_RST_GPIO, PN_RST_PIN); }
    // ==== EXTI Low Level ====
    inline void IRxClearIrqBit()    { EXTI->PR   =  EXTI_PR_PR10;        }
    inline void RplRdy_IRQEnable()  { EXTI->IMR  =  EXTI_IMR_MR10;       }
    inline void RplRdy_IRQDisable() { EXTI->IMR &= ~EXTI_IMR_MR10;       }
//
//    // SPI protocol
    uint8_t Cmd(uint8_t idCMD, uint32_t ADataLength, ...);
    void ITransmit(uint8_t *Ptr, uint32_t ALength, StartRx_t AStartRx);
    inline void WakeThread();
    void WriteByte(uint8_t AByte);
    void ReadByte()                  { WriteByte(0x00);                  }
public:
    CardState_t CardState;
    void Init();
    Pn_Frame_t PnFrame;
    // CCID interface
    MsgBox_t<ccMsg_t, CRD_MSGBOX_SZ> MsgBox;

    inline void Task();
    inline void CardTask();
//    // Inner use
    inline void IRQHandler();   // SPI IRQ Handler
    inline void RxIrqHandler(); // EXTI P70_IRQ Handler
};

extern Pn_t Pn;

#endif /* PN_H_ */
